Skip to content

fix(schedule): preserve scroll position when returning from a session - #190

Merged
GerardPaligot merged 1 commit into
mainfrom
fix/schedule-scroll-restore
Jun 11, 2026
Merged

fix(schedule): preserve scroll position when returning from a session#190
GerardPaligot merged 1 commit into
mainfrom
fix/schedule-scroll-restore

Conversation

@GerardPaligot

Copy link
Copy Markdown
Owner

Problem

In the Android app's schedule screen, scrolling through the agenda, opening a session, and pressing back reset the scroll position to the top. The selected day was kept, but the scroll within the day was lost — forcing the user to scroll back down every time.

Root cause

Navigation Compose only composes the top destination. When you open a session detail, the ScheduleList destination leaves composition (its NavBackStackEntry stays on the back stack, but the composable tree is torn down). On popBackStack() it's composed fresh.

The per-day scroll states in ScheduleGridPager were held in a plain remember:

val gridStates = remember(agendas.size) { List(agendas.size) { LazyGridState() } }

Plain remember is discarded on tear-down, so the states came back as brand-new LazyGridState() instances at offset 0. The pager state survived because rememberPagerState is saveable — which is exactly why the day was preserved but the scroll wasn't.

Fix

Hold the grid states in rememberSaveable with a listSaver that persists each day's firstVisibleItemIndex + firstVisibleItemScrollOffset, plugging into the same SaveableStateHolder mechanism the back-stack entry uses. Saving the whole list under a single key avoids the per-iteration key-collision trap you'd hit building a list of rememberLazyGridState() calls in a loop.

The restore clamps the list to the current day count, so a changed agenda day-count can't cause an IndexOutOfBounds on gridStates[page].

Also adds the compose.runtimeSaveable dependency — the module previously only used the foundation wrappers, so androidx.compose.runtime.saveable wasn't on its compile classpath.

Testing

  • ktlintCheck + detekt
  • :androidApp:lintDebug
  • Compiles for common / desktop / wasmJs / android ✅
  • ⚠️ Not yet manually verified on a device — the change is a textbook rememberrememberSaveable fix, but a quick manual pass (scroll → open session → back) would confirm.

🤖 Generated with Claude Code

The schedule grid's per-day scroll positions were held in a plain
`remember`. Navigation Compose tears the schedule destination out of
composition when opening a session detail, so on back-navigation the
`LazyGridState`s were recreated at offset 0 and the list jumped to the
top.

Hold them in `rememberSaveable` with a `listSaver` that persists each
day's first-visible index and offset, so they survive the composition
tear-down via the NavBackStackEntry's saveable state. The restore clamps
the list to the current day count to keep `gridStates[page]` indexing
safe if the agenda changes while away.

Adds the `compose.runtimeSaveable` dependency, needed to import
`rememberSaveable`/`listSaver` directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@GerardPaligot
GerardPaligot merged commit e800c1a into main Jun 11, 2026
7 checks passed
@GerardPaligot
GerardPaligot deleted the fix/schedule-scroll-restore branch June 11, 2026 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant